home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 October: Mac OS SDK / Dev.CD Oct 96 SDK / Dev.CD Oct 96 SDK2.toast / Development Kits (Disc 2) / Speech Synthesis Manager / Sample Code / SpeakToMe / SpeakToMe.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-21  |  19.7 KB  |  678 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #
  4. #    Simple Speech Synthesis Sample Application
  5. #
  6. #    SpeakToMe
  7. #
  8. #    SpeakToMe.c    -    C Source
  9. #
  10. #    Copyright © 1988, 1994, 1996 Apple Computer, Inc.
  11. #    All rights reserved.
  12. #
  13. #    Versions:    1.0                    5/96
  14. #
  15. #    Components:    SpeakToMe.c        May 20, 1996
  16. #                SpeakToMe.r        May 20, 1996
  17. #                SpeakToMe.make    May 20, 1996
  18. #
  19. #    The SpeakToMe program demonstrates of how to use the Speech Synthesis Manager
  20. #    in a simple application.  In addition to showing how to speak a string or
  21. #    a buffer of text, it exercises the API for selecting a voice and for changing
  22. #    the speaking rate and baseline pitch.
  23. #    
  24. #    The program is still a complete Macintosh application with a Main Event Loop,
  25. #    so there is the extra code to run the MEL.  
  26. #    
  27. #    There is a resource file that is necessary as well, to define the Menus, Window,
  28. #    Dialog, and Palette resources used in the program.  
  29. #
  30. #    See Sample and TESample for the general structure and MultiFinder techniques that
  31. #    we recommend that you use when building a new application.
  32. #
  33. ------------------------------------------------------------------------------*/
  34.  
  35.  /*
  36.     File SpeakToMe.c
  37.  
  38.     Version 1.0:  5/20/96    
  39.  */
  40.  
  41.  
  42. #include <Types.h>
  43. #include <Memory.h>
  44. #include <Resources.h>
  45. #include <Quickdraw.h>
  46. #include <Palettes.h>
  47. #include <Fonts.h>
  48. #include <Events.h>
  49. #include <Menus.h>
  50. #include <Windows.h>
  51. #include <Dialogs.h>
  52. #include <Desk.h>
  53. #include <OSUtils.h>
  54. #include <ToolUtils.h>
  55. #include <stdlib.h>
  56. #include <stdio.h>
  57. #include <string.h>
  58. #include <strings.h>
  59. #include <Gestalt.h>
  60. #include <Speech.h>            /* SPEECH SYNTHESIS MANAGER */
  61.     
  62. /* Constants */
  63. #define kAppleID            1000            /* resource IDs/menu IDs for Apple, */
  64. #define kFileID                1001            /*     File menu, */
  65. #define kEditID                1002            /*    Edit menu, */
  66. #define kRateID                1003            /*    Speech Rate menu, */
  67. #define kPitchID            1004            /*    Speech Pitch menu, */
  68. #define kVoiceID            1005            /*    and Voice menu */
  69.     
  70. #define kAppleM                0                /* Index for each menu in array of menu handles */
  71. #define kFileM                1
  72. #define kEditM                2
  73. #define kRateM                3
  74. #define kPitchM                4
  75. #define kVoiceM                5
  76.  
  77. #define kMenuCount            6                /* Total number of menus */
  78.  
  79. #define kWindowID            1000            /* Resource ID for main window */
  80. #define kRectID                1000            /* Resource ID for window rect */
  81. #define kAboutMeDLOG         1000            /* And Resource ID for About box dialog. */
  82. #define kErrorDLOG             1001            /* And Resource ID for Error dialog. */
  83.  
  84. #define kSpeakItem            1                /* Start speaking. */
  85. #define kPauseItem            2                /* Pause speaking. */
  86. #define kStopItem            3                /* Stop speaking. */
  87. #define kQuitItem            5                /* Quit in the menu of course. */
  88.  
  89. #define kClearItem            6                /* Clear in the Edit menu. */
  90.  
  91. #define kFastItem            1                /* Fast speaking rate. */
  92. #define kNormalItem            2                /* Normal speaking rate. */
  93. #define kSlowItem            3                /* Slow speaking rate. */
  94.  
  95. #define kHighItem            1                /* High pitch range. */
  96. #define kMidItem            2                /* Middle  pitch range. */
  97. #define kLowItem            3                /* Low  pitch range. */
  98.  
  99. #define kAboutMeCommand        1                /* Menu item in apple menu for About SpeakToMe item */    
  100.     
  101. /* Generic Globals */
  102. MenuHandle        gMenuArray[kMenuCount];
  103. Rect            gDragRect;                    /* Rectangle used to mark bounds for dragging window */
  104. Boolean            gDoneFlag;                    /* true if user has chosen Quit command */
  105. EventRecord        gMyEvent;
  106. WindowPtr        gMyWindow,
  107.                 gWhichWindow;
  108. char            gEventChar;
  109. long            gDefaultWindowColor = greenColor;
  110. long            gDefaultTextColor = blueColor;
  111.  
  112. /* Speech Synthesis Globals */
  113. short            gActiveVoiceSelection = 0;
  114. SpeechChannel    gSpeechChan;
  115. Fixed            gDefaultRate;
  116. Fixed            gDefaultPitch;
  117. Boolean            gPauseFlag = false;
  118. Boolean         gRandInitialized = false;
  119. Str255            gSpeakStr = "";
  120.  
  121. /* Function Prototypes */
  122. void SetUpMenus();
  123. OSErr ShowAboutMeDialog();
  124. void DoCommand(long int mResult);
  125. OSErr ChangeVoice (short voiceSelection);
  126. OSErr InstallVoiceList (MenuHandle mHandle);
  127. void GetText(char* textString);
  128. unsigned int RandomizeIndex(unsigned int maxValue);
  129. void PaintTheWindow(long color);
  130. void ShowErrorDialog();
  131.  
  132.     
  133. main()
  134. {
  135.     long        result;  /* result of Gestalt call */
  136.     SysEnvRec    theWorld;  /*  result of SysEnvirons call */
  137.     OSErr        error;  /* error status */
  138.     
  139.     /*
  140.     **    First check to see if the Speech Manager extension is present.  
  141.     **    If not, just beep and exit.
  142.     */
  143.     error = Gestalt(gestaltSpeechAttr, &result);
  144.     if ((error) || !(result & (1 << gestaltSpeechMgrPresent))) {  
  145.         SysBeep (50);
  146.         ExitToShell();  /* If no Speech Manager, then bail. */                
  147.         }
  148.  
  149.     /*
  150.     **    Now test the computer to see if we can do color.  
  151.     **    If not we use a black and white window.  
  152.     */
  153.     error = SysEnvirons(1, &theWorld);
  154.     if (theWorld.hasColorQD == false) {
  155.         /* If no color QD, we will use black and white. */
  156.         gDefaultWindowColor = whiteColor;
  157.         gDefaultTextColor = blackColor;
  158.         }
  159.  
  160.     InitGraf(&qd.thePort);
  161.     InitFonts();
  162.     InitWindows();
  163.     InitMenus();
  164.     InitDialogs(nil);
  165.     InitCursor();
  166.  
  167.     SetRect(&gDragRect, 4, 24, qd.screenBits.bounds.right - 4, qd.screenBits.bounds.bottom - 4);
  168.     gDoneFlag = false;  /* flag to detect when Quit command is chosen */
  169.  
  170.     /*
  171.     **    Open the window.
  172.     */
  173.     gMyWindow = GetNewWindow(kWindowID, nil, (WindowPtr) -1);
  174.     SetPort(gMyWindow);
  175.  
  176.     /*
  177.     **    Set up menus
  178.     */
  179.     SetUpMenus();
  180.     
  181.     /*
  182.     **    Get ready to draw static text
  183.     */
  184.     TextMode(srcCopy);
  185.     TextFont(geneva);
  186.     TextSize(18);
  187.     TextFace(bold);    
  188.  
  189.     /*
  190.     **    Main Event Loop
  191.     */
  192.     do {
  193.         SystemTask();
  194.  
  195.         if (WaitNextEvent(everyEvent, &gMyEvent, 5L, NULL)) {
  196.             switch (gMyEvent.what) {  /* Case on event type: */
  197.  
  198.                 case mouseDown:
  199.                     switch (FindWindow(gMyEvent.where, &gWhichWindow)) {
  200.  
  201.                         case inSysWindow:  /* desk accessory window: call Desk Manager to handle it */
  202.                             SystemClick(&gMyEvent, gWhichWindow);
  203.                             break;
  204.  
  205.                         case inMenuBar:  /* Menu bar: learn which command, then execute it. */
  206.                             DoCommand(MenuSelect(gMyEvent.where));
  207.                             break;
  208.  
  209.                         case inDrag:  /* title bar: call Window Manager to drag */
  210.                             DragWindow(gWhichWindow, gMyEvent.where, &gDragRect);
  211.                             break;
  212.  
  213.                         case inContent:  /* body of application window: */
  214.                             if (gWhichWindow != FrontWindow())
  215.                                 SelectWindow(gWhichWindow); /* make it active if not */
  216.                             break;
  217.                     }
  218.                     break;
  219.  
  220.                 case updateEvt:  /* refresh the window: */
  221.                     if ((WindowPtr) gMyEvent.message == gMyWindow) {
  222.                         BeginUpdate((WindowPtr) gMyEvent.message);
  223.  
  224.                         PaintTheWindow(gDefaultWindowColor);  /* fill in the window color */
  225.  
  226.                         MoveTo(16, 28);
  227.                         ForeColor(gDefaultTextColor);
  228.                         BackColor(gDefaultWindowColor);
  229.                         DrawString(gSpeakStr);  /* and redraw the text */
  230.  
  231.                         EndUpdate((WindowPtr) gMyEvent.message);
  232.                     }
  233.                     break;
  234.                             
  235.                 case keyDown:
  236.                 case autoKey:  /* key pressed once or held down to repeat */
  237.                     if (gMyWindow == FrontWindow()) {
  238.                         gEventChar = (gMyEvent.message & charCodeMask);  /* get the char */
  239.                         /* 
  240.                         **    If Command key down, do it as a Menu Command.
  241.                         */
  242.                         if (gMyEvent.modifiers & cmdKey)
  243.                             DoCommand(MenuKey(gEventChar));
  244.                     }
  245.                     break;
  246.  
  247.             }
  248.         }
  249.  
  250.     } while (!gDoneFlag);
  251.  
  252.     /* If Speaking then Stop */
  253.     if (SpeechBusy()) StopSpeech(gSpeechChan);
  254.     DisposeSpeechChannel(gSpeechChan);
  255.  
  256.     DisposeWindow (gMyWindow);
  257. }
  258.  
  259.  
  260. /*
  261. **    Read menu descriptions from resource file into memory 
  262. **    and store handles in menu array.
  263. **    Insert into MenuBar and draw.
  264. */
  265. void SetUpMenus()
  266. {
  267.     int i;
  268.  
  269.     gMenuArray[kAppleM] = GetMenu(kAppleID);  /* Read Apple menu from resource file */
  270.     AddResMenu(gMenuArray[kAppleM], 'DRVR');  /* Add desk accessory names to Apple menu */
  271.     gMenuArray[kFileM] = GetMenu(kFileID);    /* Read file menu from resource file */
  272.     gMenuArray[kEditM] = GetMenu(kEditID);    /* Read edit menu from resource file */
  273.     gMenuArray[kRateM] = GetMenu(kRateID);    /* Read rate menu from resource file */
  274.     gMenuArray[kPitchM] = GetMenu(kPitchID);  /* Read pitch menu from resource file */
  275.     gMenuArray[kVoiceM] = GetMenu(kVoiceID);  /* Read voice menu from resource file */
  276.  
  277.     /* Dynamically insert the available voices into the voice menu */
  278.     InstallVoiceList(gMenuArray[kVoiceM]);
  279.  
  280.     for (i = 0; i < kMenuCount; i++) 
  281.         InsertMenu(gMenuArray[i], 0);  /* Install menus in menu bar... */
  282.     
  283.     DrawMenuBar();  /* and draw menu bar */
  284. }
  285.  
  286.  
  287. /*    
  288. **    Display the dialog box in response to the 'About SpeakIt' menu item
  289. */
  290. OSErr ShowAboutMeDialog()
  291. {
  292.     DialogPtr    theDialog;
  293.     short        itemHit;
  294.     OSErr        error;
  295.  
  296.     /* Before putting up the dialog, start speaking a brief message */
  297.     /* This is the simplest way to produce speech using the synthesis API */
  298.     /* The text in the string will be spoken using the system default voice */
  299.     error = SpeakString("\pHello from the Speech Group!");
  300.     if (!error) { /* If no error then do the dialog */
  301.         theDialog = GetNewDialog(kAboutMeDLOG, nil, (WindowPtr) -1);
  302.         ModalDialog(nil, &itemHit);
  303.         DisposDialog(theDialog);
  304.         }
  305.     return error;
  306. }
  307.  
  308.  
  309. /*    
  310. **    Display the dialog box before exiting on error condition
  311. */
  312. void ShowErrorDialog()
  313. {
  314.     DialogPtr    theDialog;
  315.     short        itemHit;
  316.  
  317.     theDialog = GetNewDialog(kErrorDLOG, nil, (WindowPtr) -1);
  318.     ModalDialog(nil, &itemHit);
  319.     DisposDialog(theDialog);
  320. }
  321.  
  322.  
  323. /*
  324. **    Execute menu command specified by mResult,
  325. **    the result of MenuSelect
  326. */
  327. void DoCommand(long int mResult)
  328. {
  329.     short    theItem,  /* menu item number from mResult low-order word */
  330.             theMenu;  /* menu number from mResult high-order word */
  331.     Str255    name;  /* desk accessory name */
  332.     int        temp;  /* dummy variable for calling open on desk accessory */
  333.     Fixed    newRate,  /* new speaking rate */
  334.             newPitch;  /* new baseline pitch */
  335.     OSErr    error = noErr;  /* error status */
  336.  
  337.     theItem = LoWord(mResult);  /* Call Toolbox Utility routines to */
  338.     theMenu = HiWord(mResult);  /* set menu item number and menu */
  339.     
  340.     switch (theMenu) {  /* Switch on menu ID: */
  341.  
  342.         case kAppleID:
  343.             if (theItem == kAboutMeCommand) {
  344.                 error = ShowAboutMeDialog();
  345.             }
  346.             else {
  347.                     GetItem(gMenuArray[kAppleM], theItem, name);
  348.                     temp = OpenDeskAcc(name);
  349.                     SetPort(gMyWindow);
  350.             }
  351.             break;
  352.  
  353.         case kFileID:
  354.             if (theItem == kSpeakItem) {      
  355.                 if (gPauseFlag) {  /* If paused then continue speaking */            
  356.                     error = ContinueSpeech(gSpeechChan);
  357.                     /* Reset the pause flag and uncheck the pause item */
  358.                     gPauseFlag = false;  
  359.                     CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
  360.                     }
  361.                 else {  /* We're not paused, so speak a new phrase: */
  362.                     GetText(gSpeakStr);     /* Generate a text string */
  363.                     
  364.                     c2pstr(gSpeakStr);     /* Convert to a Pascal string */                                      
  365.                     PaintTheWindow(gDefaultWindowColor);  /* Paint over the old text... */
  366.                     MoveTo(16, 28);
  367.                     ForeColor(gDefaultTextColor);
  368.                     BackColor(gDefaultWindowColor);
  369.                     DrawString(gSpeakStr);  /* and draw in the new text */
  370.                     
  371.                     /* Now go ahead and start speaking */
  372.                     error = SpeakText(gSpeechChan, &gSpeakStr[1], gSpeakStr[0]);
  373.                     }
  374.                 }
  375.             else if (theItem == kPauseItem) {
  376.                 /* If already paused then continue speaking */
  377.                 if (gPauseFlag) error = ContinueSpeech(gSpeechChan);
  378.                 /* Otherwise, pause immediately */
  379.                 else error = PauseSpeechAt(gSpeechChan, kImmediate);
  380.                 /* Invert the pause flag and check/uncheck the pause item */
  381.                 gPauseFlag = !gPauseFlag;
  382.                 CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
  383.                 }
  384.             else if (theItem == kStopItem) {
  385.                 /* Always try to stop speaking */
  386.                 error = StopSpeech(gSpeechChan);
  387.                 /* Reset the pause flag and uncheck the pause item */
  388.                 gPauseFlag = false;
  389.                 CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
  390.                 }
  391.             else if (theItem == kQuitItem)
  392.                 gDoneFlag = true;
  393.             break;
  394.  
  395.         case kEditID:
  396.             /* Paint over the text display */
  397.             if (theItem == kClearItem) PaintTheWindow(gDefaultWindowColor);
  398.             break;
  399.  
  400.         case kRateID:
  401.             if (theItem == kFastItem) {
  402.                 /* Speed up the speaking rate */
  403.                 newRate = gDefaultRate * 1.3;  /* Increase default rate by 30% */
  404.                 error = SetSpeechRate(gSpeechChan, newRate);  /* Set new rate */                    
  405.                 /* Check the fast menu item and uncheck the others */
  406.                 CheckItem(gMenuArray[kRateM], kFastItem, true);
  407.                 CheckItem(gMenuArray[kRateM], kNormalItem, false);
  408.                 CheckItem(gMenuArray[kRateM], kSlowItem, false);
  409.                 }
  410.             else if (theItem == kNormalItem) {
  411.                 /* Reset the speaking rate */
  412.                 error = SetSpeechRate(gSpeechChan, gDefaultRate);     /* Set default rate */
  413.                 /* Check the normal menu item and uncheck the others */
  414.                 CheckItem(gMenuArray[kRateM], kFastItem, false);
  415.                 CheckItem(gMenuArray[kRateM], kNormalItem, true);
  416.                 CheckItem(gMenuArray[kRateM], kSlowItem, false);
  417.                 }
  418.             else if (theItem == kSlowItem){
  419.                 /* Slow down the speaking rate */
  420.                 newRate = gDefaultRate * 0.7;                    /* Decrease default rate by 30% */
  421.                 error = SetSpeechRate(gSpeechChan, newRate);     /* Set new rate */          
  422.                 /* Check the slow menu item and uncheck the others */
  423.                 CheckItem(gMenuArray[kRateM], kFastItem, false);
  424.                 CheckItem(gMenuArray[kRateM], kNormalItem, false);
  425.                 CheckItem(gMenuArray[kRateM], kSlowItem, true);
  426.                 }
  427.             break;
  428.             
  429.         case kPitchID:
  430.             if (theItem == kHighItem) {
  431.                 /* Make the baseline pitch higher */
  432.                 newPitch = gDefaultPitch + Long2Fix(6L);              /* Raise pitch by 1/2 octave */
  433.                 error = SetSpeechPitch(gSpeechChan, newPitch);        /* Set new pitch */ 
  434.                 /* Check the high menu item and uncheck the others */
  435.                 CheckItem(gMenuArray[kPitchM], kHighItem, true);
  436.                 CheckItem(gMenuArray[kPitchM], kMidItem, false);
  437.                 CheckItem(gMenuArray[kPitchM], kLowItem, false);
  438.                 }
  439.             else if (theItem == kMidItem) {
  440.                 /* Reset the baseline pitch */
  441.                 error = SetSpeechPitch(gSpeechChan, gDefaultPitch);    /* Set default pitch */
  442.                 /* Check the middle menu item and uncheck the others */
  443.                 CheckItem(gMenuArray[kPitchM], kHighItem, false);
  444.                 CheckItem(gMenuArray[kPitchM], kMidItem, true);
  445.                 CheckItem(gMenuArray[kPitchM], kLowItem, false);
  446.                 }
  447.             else if (theItem == kLowItem){
  448.                 /* Make the baseline pitch lower */
  449.                 newPitch = gDefaultPitch - Long2Fix(6L);            /* Lower pitch by 1/2 octave */
  450.                 error = SetSpeechPitch(gSpeechChan, newPitch);        /* Set new rate */         
  451.                 /* Check the low menu item and uncheck the others */
  452.                 CheckItem(gMenuArray[kPitchM], kHighItem, false);
  453.                 CheckItem(gMenuArray[kPitchM], kMidItem, false);
  454.                 CheckItem(gMenuArray[kPitchM], kLowItem, true);
  455.                 }
  456.             break;
  457.             
  458.         case kVoiceID:
  459.             error = ChangeVoice(theItem);
  460.             break;
  461.     }
  462.     
  463.     if (error) {
  464.         ShowErrorDialog();  /* Display error dialog... */
  465.         gDoneFlag = true;   /* ... and terminate the program */
  466.         }
  467.         
  468.     HiliteMenu(0);  /* Unhighlight menu title */
  469.                     /* (highlighted by MenuSelect) */
  470. }
  471.  
  472.  
  473. /*
  474. **    Respond to Voice Change selection.
  475. */
  476. OSErr ChangeVoice(short voiceSelection) {
  477.     VoiceSpec    voice;  /* voice specification for selected voice */
  478.     OSErr        error;  /* error status */
  479.  
  480.     if (voiceSelection == gActiveVoiceSelection)
  481.         return noErr;    /* Change the voice only if necessary */
  482.  
  483.     /* Get the requested voice spec */
  484.     error = GetIndVoice (voiceSelection, &voice);
  485.     
  486.     /* Get rid of the old speech channel */
  487.     if ((error == noErr) && (gActiveVoiceSelection)) {        
  488.         error = DisposeSpeechChannel(gSpeechChan);  // Release the channel
  489.         }
  490.         
  491.     /* Allocate a new speech channel */
  492.     if (error == noErr) error = NewSpeechChannel(&voice, &gSpeechChan);
  493.     
  494.     /* Set the default speaking rate and pitch */
  495.     if (error == noErr) error = GetSpeechRate(gSpeechChan, &gDefaultRate);
  496.     if (error == noErr) error = GetSpeechPitch(gSpeechChan, &gDefaultPitch);
  497.         
  498.     /* Check and un-check the correct menu items */
  499.     if (error == noErr) {
  500.         gPauseFlag = false;
  501.         CheckItem(gMenuArray[kFileM], kPauseItem, gPauseFlag);
  502.  
  503.         CheckItem(gMenuArray[kRateM], kFastItem, false);
  504.         CheckItem(gMenuArray[kRateM], kNormalItem, true);
  505.         CheckItem(gMenuArray[kRateM], kSlowItem, false);
  506.  
  507.         CheckItem(gMenuArray[kPitchM], kHighItem, false);
  508.         CheckItem(gMenuArray[kPitchM], kMidItem, true);
  509.         CheckItem(gMenuArray[kPitchM], kLowItem, false);
  510.  
  511.         if (gActiveVoiceSelection != 0) CheckItem (gMenuArray[kVoiceM], gActiveVoiceSelection, false);
  512.         CheckItem (gMenuArray[kVoiceM], voiceSelection, true);
  513.         gActiveVoiceSelection = voiceSelection;  /* Assign the global voice selection */
  514.         }
  515.     
  516.     return error;
  517. }
  518.  
  519.  
  520. /*
  521. **    Create Menu entries for each available voice.
  522. **    Select System Default voice.
  523. */
  524. OSErr InstallVoiceList(MenuHandle mHandle) {
  525.     short                voiceCount, i;
  526.     VoiceSpec            voice;
  527.     VoiceDescription    desc, defaultDesc;
  528.     OSErr                error;
  529.  
  530.     /* Get the description for the system default voice */
  531.     defaultDesc.length = sizeof(VoiceDescription);
  532.     error = GetVoiceDescription(NULL, &defaultDesc, defaultDesc.length);
  533.         
  534.     /* Count the number of available voices (for all synthesizers) */
  535.     error = CountVoices (&voiceCount);
  536.     if (error != noErr) return error;
  537.     
  538.     if (voiceCount > 0) {
  539.         /* Install each voice in the Voice menu and make the system default the active voice */
  540.         for (i = 1; i <= voiceCount; ++i) {
  541.             // get the next indexed voice
  542.             error = GetIndVoice (i, &voice);
  543.             if (error == noErr) {
  544.                 desc.length = sizeof (VoiceDescription);
  545.                 error = GetVoiceDescription (&voice, &desc, desc.length);
  546.                 if (error == noErr) {
  547.                     /* Install the menu first with blank text (can't be empty) so that we */
  548.                     /* don't have any menu metacharacters (like parentheses or slash, etc.) */
  549.                     /* that may be in the voice name and get interpreted by the Menu Manager */ 
  550.                     AppendMenu(mHandle, "\p ");
  551.                     /* Get the item number of the newly added menu */
  552.                     /* create the menu item to be inserted using the voice name */
  553.                     SetItem (mHandle, i, desc.name);
  554.                     if ((desc.voice.id == defaultDesc.voice.id) /* Compare voice being added to default voice */
  555.                         && (desc.language == defaultDesc.language)) /* voice IDs are only unique within a given language */
  556.                         ChangeVoice(i); /* Make the system default the active selection    */
  557.                 }
  558.             }
  559.         } /* end of for loop through voices */
  560.     } /* end of VoiceCount check */
  561.  
  562.     return error;
  563. }
  564.  
  565.  
  566. /*
  567. **    Create string to speak from predefined arrays. 
  568. **    String will be a random variation on the old adage:
  569. **  "A bird in the hand is worth 2 in the bush."
  570. */
  571. void GetText(char* textToSpeak) {
  572.     /* Set up string arrays */
  573.     const char    *nounChoices[] = {
  574.                     "bird",
  575.                     "fish",
  576.                     "snake",
  577.                     "weasel",
  578.                     "cat",
  579.                     "pig",
  580.                     "worm",
  581.                     "fly",
  582.                     "fox",
  583.                     "monkey",
  584.                     0
  585.                     };
  586.  
  587.     const char    *containerChoices[] = {
  588.                     "hand",
  589.                     "pan",
  590.                     "basket",
  591.                     "barn",
  592.                     "bag",
  593.                     "sty",
  594.                     "hole",
  595.                     "jar",
  596.                     "coop",
  597.                     "zoo",
  598.                     0
  599.                     };
  600.                     
  601.     const char    *numberChoices[] = {
  602.                     "none",
  603.                     "2",
  604.                     "3",
  605.                     "4",
  606.                     "5",
  607.                     "6",
  608.                     "7",
  609.                     "8",
  610.                     "9",
  611.                     "10",
  612.                     0
  613.                     };
  614.  
  615.     const char    *locationChoices[] = {
  616.                     "bush",
  617.                     "water",
  618.                     "grass",
  619.                     "den",
  620.                     "tree",
  621.                     "mud",
  622.                     "compost",
  623.                     "ointment",
  624.                     "woods",
  625.                     "jungle",
  626.                     0
  627.                     };
  628.     /* Generate string by making random choices from arrays */
  629.     textToSpeak[0] = '\0';
  630.     strcpy(textToSpeak, "A ");
  631.     strcat(textToSpeak, nounChoices[RandomizeIndex(9)]);
  632.     strcat(textToSpeak, " in the ");
  633.     strcat(textToSpeak, containerChoices[RandomizeIndex(9)]);
  634.     strcat(textToSpeak, " is worth ");
  635.     strcat(textToSpeak, numberChoices[RandomizeIndex(9)]);
  636.     strcat(textToSpeak, " in the ");
  637.     strcat(textToSpeak, locationChoices[RandomizeIndex(9)]);
  638.     strcat(textToSpeak, ".");
  639. }
  640.  
  641.  
  642. /*
  643. **    Return a random index between 0 and the maxValue parameter. 
  644. **    Uses the standard C "rand" function with the current time as a seed value.
  645. */
  646. unsigned int RandomizeIndex(unsigned int maxValue) {
  647.     unsigned long seedVal;
  648.     unsigned int result,randFactor;
  649.     
  650.     /* Divide up the range of potential values based on the desired maximum value */
  651.     randFactor = RAND_MAX / maxValue;
  652.     /* Initialize the random sequence with a seed value derived from the current time */
  653.     if (!gRandInitialized) {    
  654.         ReadDateTime(&seedVal);    
  655.         srand((unsigned int)seedVal);
  656.         gRandInitialized = true;
  657.         }    
  658.     result = rand()/randFactor;
  659.  
  660.     return result;    
  661. }
  662.  
  663.  
  664. /*
  665. **    Simple function for filling in the window color 
  666. */
  667. void PaintTheWindow(long color)
  668. {
  669.     Handle        resource;
  670.     
  671.     resource = GetResource('RECT', kRectID);
  672.     if ( resource != nil ) {
  673.         ForeColor(color);
  674.         PaintRect(*(Rect**) resource);
  675.     }
  676. }
  677.  
  678.